using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using HslCommunication;
using HslCommunication.Core;
using HslCommunicationEx.OpcUa.Client;
using Opc.Ua;
using Opc.Ua.Client;
using Catastrophe.AutoSulution.TaskM;

namespace HslCommunicationEx.OpcUa
{
	public class OpcUaSiemens
	{
		private readonly SimpleHybirdLock _simpleLockReadWrite;

		public readonly int MaxRequestCount = 100;

		protected Session MSession;

		protected ushort NameSpaceIndex;

		private string _url;

		public CertificateValidationEventHandler CertificateValidationNotification = null;

		public KeepAliveEventHandler KeepAliveNotification = null;

		private string[] _nameSpaceAry;

		public bool IsDisposed => this.MSession?.Disposed ?? false;

		public int ReadTimeout { get; set; } = 3000;


		public int WriteTimeout { get; set; } = 3000;


		public string[] NameSpaceAry => this._nameSpaceAry;

		public bool IsConnected { get; set; }

		public OpcUaSiemens()
		{
			this._simpleLockReadWrite = new SimpleHybirdLock();
		}

		~OpcUaSiemens()
		{
			this.Close();
		}

		public OperateResult Connect(string url, string nameSpace, int timeout = 5000, string userName = "", string password = "", string secPolicy = "http://opcfoundation.org/UA/SecurityPolicy#None", MessageSecurityMode msgSecMode = MessageSecurityMode.None, ServerIdentityEntity identity = null)
		{
			this._url = url;
			if (url == null)
			{
				throw new ArgumentNullException("url");
			}
			if (nameSpace == null)
			{
				throw new ArgumentNullException("nameSpace");
			}
			bool userAuth = !string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password);
			return this.Connect(url, nameSpace, secPolicy, msgSecMode, timeout, userAuth, userName, password, identity);
		}

		public OperateResult Connect(string url, string nameSpace, string secPolicy, MessageSecurityMode msgSecMode, int timeout, bool userAuth, string userName, string password, ServerIdentityEntity identity = null)
		{
			this._url = url;
			ushort nameSpaceIndex = 0;
			OperateResult<string[]> operateResult = TaskManager.StartToDo(delegate
			{
				ApplicationConfiguration applicationConfiguration = OpcUaUtil.CreateClientConfiguration();
				applicationConfiguration.CertificateValidator.CertificateValidation += Notificatio_CertificateValidation;
				EndpointDescription description = OpcUaUtil.CreateEndpointDescription(url, secPolicy, msgSecMode, identity);
				EndpointConfiguration configuration = EndpointConfiguration.Create(applicationConfiguration);
				ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, description, configuration);
				UserIdentity identity2 = (userAuth ? new UserIdentity(userName, password) : new UserIdentity());
				applicationConfiguration.CertificateValidator.Update(applicationConfiguration);
				this.MSession = Session.Create(applicationConfiguration, endpoint, updateBeforeConnect: false, "MySession", 5000u, identity2, null).Result;
				bool connected = this.MSession.Connected;
				this.MSession.KeepAlive += Notification_KeepAlive;
				ReadValueIdCollection variableIds = new ReadValueIdCollection
				{
					new ReadValueId
					{
						NodeId = new NodeId("ns=0;i=" + 2255u),
						AttributeId = 13u
					}
				};
				this.MSession.ReadValues(variableIds, out var results);
				if (results.Count != 1 || results[0].Value.GetType() != typeof(string[]))
				{
					throw new Exception("Reading namespace table returned unexptected result");
				}
				string[] array = (string[])results[0].Value;
				for (ushort num = 0; num < array.Length; num = (ushort)(num + 1))
				{
					if (array[num] == nameSpace)
					{
						nameSpaceIndex = num;
					}
				}
				return array;
			}, timeout);
			if (!operateResult.IsSuccess)
			{
				this.MSession?.Close();
				this.MSession?.Dispose();
				this.MSession = null;
				this.IsConnected = false;
				return new OperateResult
				{
					Message = operateResult.Message
				};
			}
			this._nameSpaceAry = operateResult.Content;
			this.IsConnected = this.MSession?.Connected ?? false;
			OperateResult operateResult2 = new OperateResult
			{
				IsSuccess = this.IsConnected,
				Message = ""
			};
			if (nameSpaceIndex == 0)
			{
				operateResult2.Message = "获取NameSpace失败!";
			}
			else
			{
				this.NameSpaceIndex = nameSpaceIndex;
			}
			return operateResult2;
		}

		public OperateResult Connect(string url, string nameSpace, EndpointDescription endpointDescription, int timeout = 5000, string userName = "", string password = "")
		{
			if (url == null)
			{
				throw new ArgumentNullException("url");
			}
			if (nameSpace == null)
			{
				throw new ArgumentNullException("nameSpace");
			}
			if (endpointDescription == null)
			{
				throw new ArgumentNullException("endpointDescription");
			}
			this._url = url;
			bool userAuth = !string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password);
			ushort nameSpaceIndex = 0;
			OperateResult<string[]> operateResult = TaskManager.StartToDo(delegate
			{
				ApplicationConfiguration applicationConfiguration = OpcUaUtil.CreateClientConfiguration();
				applicationConfiguration.CertificateValidator.CertificateValidation += Notificatio_CertificateValidation;
				EndpointConfiguration configuration = EndpointConfiguration.Create(applicationConfiguration);
				ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, configuration);
				UserIdentity identity = (userAuth ? new UserIdentity(userName, password) : new UserIdentity());
				applicationConfiguration.CertificateValidator.Update(applicationConfiguration);
				this.MSession = Session.Create(applicationConfiguration, endpoint, updateBeforeConnect: false, "MySession", 5000u, identity, null).Result;
				bool connected = this.MSession.Connected;
				this.MSession.KeepAlive += Notification_KeepAlive;
				ReadValueIdCollection variableIds = new ReadValueIdCollection
				{
					new ReadValueId
					{
						NodeId = new NodeId("ns=0;i=" + 2255u),
						AttributeId = 13u
					}
				};
				this.MSession.ReadValues(variableIds, out var results);
				if (results.Count != 1 || results[0].Value.GetType() != typeof(string[]))
				{
					throw new Exception("Reading namespace table returned unexptected result");
				}
				string[] array = (string[])results[0].Value;
				for (ushort num = 0; num < array.Length; num = (ushort)(num + 1))
				{
					if (array[num] == nameSpace)
					{
						nameSpaceIndex = num;
					}
				}
				return array;
			}, timeout);
			if (!operateResult.IsSuccess)
			{
				this.MSession?.Close();
				this.MSession?.Dispose();
				this.MSession = null;
				this.IsConnected = false;
				return new OperateResult
				{
					Message = operateResult.Message
				};
			}
			this._nameSpaceAry = operateResult.Content;
			this.IsConnected = this.MSession?.Connected ?? false;
			OperateResult operateResult2 = new OperateResult
			{
				IsSuccess = this.IsConnected,
				Message = ""
			};
			if (nameSpaceIndex == 0)
			{
				operateResult2.Message = "获取NameSpace失败!";
			}
			else
			{
				this.NameSpaceIndex = nameSpaceIndex;
			}
			return operateResult2;
		}

		public OperateResult<EndpointDescriptionCollection> GetEndpoints(string serverUrl)
		{
			OperateResult<EndpointDescriptionCollection> operateResult = new OperateResult<EndpointDescriptionCollection>();
			Uri discoveryUrl = new Uri(serverUrl);
			try
			{
				DiscoveryClient discoveryClient = DiscoveryClient.Create(discoveryUrl);
				EndpointDescriptionCollection endpoints = discoveryClient.GetEndpoints(null);
				operateResult.IsSuccess = true;
				operateResult.Content = endpoints;
				return operateResult;
			}
			catch (Exception ex)
			{
				operateResult.Message = ex.ToString();
				return operateResult;
			}
		}

		public OperateResult<ApplicationDescriptionCollection> FindServers(string discoveryUrl)
		{
			OperateResult<ApplicationDescriptionCollection> operateResult = new OperateResult<ApplicationDescriptionCollection>();
			Uri discoveryUrl2 = new Uri(discoveryUrl);
			try
			{
				DiscoveryClient discoveryClient = DiscoveryClient.Create(discoveryUrl2);
				ApplicationDescriptionCollection content = discoveryClient.FindServers(null);
				operateResult.IsSuccess = true;
				operateResult.Content = content;
				return operateResult;
			}
			catch (Exception ex)
			{
				operateResult.Message = ex.ToString();
				return operateResult;
			}
		}

		public void ReadNameSpace(string nameSpace)
		{
			ReadValueIdCollection variableIds = new ReadValueIdCollection
			{
				new ReadValueId
				{
					NodeId = new NodeId("ns=0;i=" + 2255u, this.NameSpaceIndex),
					AttributeId = 13u
				}
			};
			this.MSession.ReadValues(variableIds, out var results);
			if (results.Count != 1 || results[0].Value.GetType() != typeof(string[]))
			{
				throw new Exception("Reading namespace table returned unexptected result");
			}
			string[] array = (string[])results[0].Value;
			for (ushort num = 0; num < array.Length; num = (ushort)(num + 1))
			{
				if (array[num] == nameSpace)
				{
					this.NameSpaceIndex = num;
				}
			}
		}

		private void Notificatio_CertificateValidation(CertificateValidator certificate, CertificateValidationEventArgs e)
		{
			e.Accept = true;
		}

		private void Notification_KeepAlive(ISession session, KeepAliveEventArgs e)
		{
			if (ServiceResult.IsBad(e.Status))
			{
				if (session != null)
				{
					session.Dispose();
					session.KeepAlive -= Notification_KeepAlive;
				}
				this.IsConnected = false;
			}
			this.KeepAliveNotification?.Invoke(session, e);
		}

		public OperateResult<DataValue> ReadSingle(string adr)
		{
			OperateResult<List<DataValue>> operateResult = this.ReadMulti(new List<string> { adr });
			if (operateResult.Content == null)
			{
				return new OperateResult<DataValue>
				{
					IsSuccess = false
				};
			}
			return new OperateResult<DataValue>
			{
				IsSuccess = operateResult.IsSuccess,
				Message = operateResult.Message,
				Content = operateResult.Content.FirstOrDefault()
			};
		}

		public OperateResult<List<string>> ReadMultiStr(List<string> dataAdr)
		{
			List<string> list = new List<string>();
			OperateResult<List<DataValue>> operateResult = this.ReadMulti(dataAdr);
			if (operateResult.IsSuccess)
			{
				foreach (DataValue item in operateResult.Content)
				{
					list.Add(item.Value?.ToString());
				}
			}
			return new OperateResult<List<string>>
			{
				IsSuccess = operateResult.IsSuccess,
				Content = list,
				Message = operateResult.Message
			};
		}

		public OperateResult<List<DataValue>> ReadMulti(List<string> dataAdr)
		{
			ReadValueIdCollection readValueIdCollection = new ReadValueIdCollection();
			foreach (string item in dataAdr)
			{
				readValueIdCollection.Add(new ReadValueId
				{
					NodeId = new NodeId(item, this.NameSpaceIndex),
					AttributeId = 13u
				});
			}
			if (this.MSession == null || !this.IsConnected)
			{
				return new OperateResult<List<DataValue>>
				{
					IsSuccess = false,
					Message = "未建立连接"
				};
			}
			List<DataValue> dataValu = new List<DataValue>();
			OperateResult operateResult = this.ReadNodesRecur(readValueIdCollection, ref dataValu);
			if (dataValu.Count != dataAdr.Count)
			{
				return new OperateResult<List<DataValue>>
				{
					Message = "请求数量与返回数量不符"
				};
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < dataValu.Count; i++)
			{
				if (!StatusCode.IsGood(dataValu[i].StatusCode))
				{
					StringBuilder stringBuilder2 = stringBuilder;
					StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(16, 3, stringBuilder2);
					handler.AppendLiteral("Adr:");
					handler.AppendFormatted(dataAdr[i]);
					handler.AppendLiteral("|StatusCode:");
					handler.AppendFormatted(dataValu[i].StatusCode);
					handler.AppendFormatted(Environment.NewLine);
					stringBuilder2.Append(ref handler);
				}
			}
			return new OperateResult<List<DataValue>>
			{
				IsSuccess = (stringBuilder.ToString() == ""),
				Message = stringBuilder.ToString(),
				Content = dataValu
			};
		}

		private OperateResult ReadNodesRecur(ReadValueIdCollection nodesBuf, ref List<DataValue> dataValu)
		{
			try
			{
				if (this.MSession == null || !this.IsConnected)
				{
					return new OperateResult
					{
						IsSuccess = false,
						Message = "未建立连接"
					};
				}
				if (nodesBuf.Count > this.MaxRequestCount)
				{
					ReadValueId[] array = new ReadValueId[this.MaxRequestCount];
					Array.Copy(nodesBuf.ToArray(), 0, array, 0, this.MaxRequestCount);
					nodesBuf.RemoveRange(0, this.MaxRequestCount);
					OperateResult<DataValueCollection> operateResult = this.SessionRead(array);
					if (!operateResult.IsSuccess)
					{
						return new OperateResult
						{
							IsSuccess = false,
							Message = operateResult.Message
						};
					}
					DataValueCollection content = operateResult.Content;
					dataValu.AddRange(content);
					this.ReadNodesRecur(nodesBuf, ref dataValu);
					return new OperateResult
					{
						IsSuccess = true
					};
				}
				OperateResult<DataValueCollection> operateResult2 = this.SessionRead(nodesBuf);
				if (operateResult2.Content == null)
				{
					return new OperateResult
					{
						Message = operateResult2.Message
					};
				}
				dataValu.AddRange(operateResult2.Content);
				return new OperateResult
				{
					IsSuccess = operateResult2.IsSuccess,
					Message = operateResult2.Message
				};
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private OperateResult<DataValueCollection> SessionRead(ReadValueIdCollection nodesToRead)
		{
			try
			{
				this._simpleLockReadWrite.Enter();
				Tuple<Task<DataValueCollection>, Exception> tuple = TaskManager.StartTo(delegate
				{
					this.MSession.ReadValues(nodesToRead, out var results);
					return results;
				}, this.ReadTimeout);
				if (tuple.Item2 != null)
				{
					if (tuple.Item2.InnerException != null && tuple.Item2.InnerException!.Message.Equals("BadSecureChannelClosed"))
					{
						this.IsConnected = false;
					}
					return new OperateResult<DataValueCollection>
					{
						IsSuccess = false,
						Message = tuple.Item2.ToString()
					};
				}
				return new OperateResult<DataValueCollection>
				{
					IsSuccess = true,
					Content = tuple.Item1.Result
				};
			}
			catch (Exception ex)
			{
				return new OperateResult<DataValueCollection>
				{
					IsSuccess = false,
					Message = ex.ToString()
				};
			}
			finally
			{
				this._simpleLockReadWrite.Leave();
			}
		}

		public OperateResult WriteSingle(string adr, dynamic value)
		{
			WriteValueCollection writeValueCollection = new WriteValueCollection();
			NodeId nodeId = new NodeId(adr, this.NameSpaceIndex);
			dynamic type = value.GetType();
			Variant value2 = new Variant(Convert.ChangeType(value, type));
			WriteValue item = new WriteValue
			{
				Value = new DataValue(value2),
				NodeId = nodeId,
				AttributeId = 13u
			};
			writeValueCollection.Add(item);
			return this.WriteMuti(writeValueCollection);
		}

		public OperateResult WriteMuti(List<DataValueEntity> updateEntities)
		{
			if (updateEntities == null)
			{
				throw new ArgumentNullException("updateEntities");
			}
			WriteValueCollection writeValueCollection = new WriteValueCollection();
			foreach (DataValueEntity updateEntity in updateEntities)
			{
				WriteValue writeValue = new WriteValue();
				writeValue.Value = new DataValue(new Variant(updateEntity.Value));
				writeValue.NodeId = new NodeId(updateEntity.Adr, this.NameSpaceIndex);
				writeValue.AttributeId = 13u;
				WriteValue item = writeValue;
				writeValueCollection.Add(item);
			}
			return this.WriteMuti(writeValueCollection);
		}

		public OperateResult WriteMuti(WriteValueCollection values)
		{
			try
			{
				this._simpleLockReadWrite.Enter();
				Tuple<Task<StatusCodeCollection>, Exception> tuple = TaskManager.StartTo(delegate
				{
					StatusCodeCollection results = null;
					this.MSession?.Write(null, values, out results, out var _);
					return results;
				}, this.WriteTimeout);
				if (tuple.Item2 != null)
				{
					return new OperateResult<DataValueCollection>
					{
						IsSuccess = false,
						Message = tuple.Item2.ToString()
					};
				}
				if (tuple.Item1.Result == null)
				{
					return new OperateResult
					{
						Message = "StatusCodeCollection return null"
					};
				}
				OperateResult operateResult = new OperateResult
				{
					IsSuccess = true
				};
				for (int i = 0; i < tuple.Item1.Result.Count; i++)
				{
					if (StatusCode.IsBad(tuple.Item1.Result[i]))
					{
						operateResult.IsSuccess = false;
						string message = operateResult.Message;
						DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(6, 3);
						defaultInterpolatedStringHandler.AppendLiteral("index=");
						defaultInterpolatedStringHandler.AppendFormatted(i);
						defaultInterpolatedStringHandler.AppendFormatted(StatusCode.LookupSymbolicId(tuple.Item1.Result[i].Code));
						defaultInterpolatedStringHandler.AppendFormatted(Environment.NewLine);
						operateResult.Message = message + defaultInterpolatedStringHandler.ToStringAndClear();
					}
				}
				return operateResult;
			}
			catch (Exception ex)
			{
				return new OperateResult
				{
					IsSuccess = false,
					Message = ex.ToString()
				};
			}
			finally
			{
				this._simpleLockReadWrite.Leave();
			}
		}

		public MethodCallResult MethodCall(string path, string fullName, params object[] inputArguments)
		{
			try
			{
				DiagnosticInfoCollection diagnosticInfos = null;
				CallMethodRequestCollection callMethodRequestCollection = new CallMethodRequestCollection();
				CallMethodResultCollection results = new CallMethodResultCollection();
				CallMethodRequest callMethodRequest = new CallMethodRequest();
				callMethodRequest.MethodId = new NodeId(fullName, this.NameSpaceIndex);
				callMethodRequest.ObjectId = new NodeId(path, this.NameSpaceIndex);
				if (inputArguments != null)
				{
					foreach (object value in inputArguments)
					{
						callMethodRequest.InputArguments.Add(new Variant(value));
					}
				}
				callMethodRequestCollection.Add(callMethodRequest);
				this.MSession.Call(null, callMethodRequestCollection, out results, out diagnosticInfos);
				return new MethodCallResult(results);
			}
			catch (Exception ex)
			{
				throw ex;
			}
			finally
			{
			}
		}

		public void Close()
		{
			try
			{
				if (this.MSession != null)
				{
					this.MSession.KeepAlive -= Notification_KeepAlive;
				}
				Session mSession = this.MSession;
				if (mSession != null && !mSession.Disposed)
				{
					this.MSession?.Close(5000);
					this.MSession?.Dispose();
					this.MSession = null;
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private bool CheckNet(int timout = 1000)
		{
			try
			{
				Ping ping = new Ping();
				string[] array = this._url.Split(':');
				if (array.Length > 1)
				{
					string ipString = array[1].Replace("//", "");
					PingReply pingReply = ping.Send(IPAddress.Parse(ipString), timout);
					return pingReply != null && pingReply.Status == IPStatus.Success;
				}
				return false;
			}
			catch (Exception)
			{
				return false;
			}
		}

		public bool Ping(string ip, int timeout = 2000)
		{
			Ping ping = new Ping();
			try
			{
				PingReply pingReply = ping.Send(ip, timeout);
				if (pingReply != null && pingReply.Status == IPStatus.Success)
				{
					return true;
				}
				return false;
			}
			catch (Exception)
			{
				return false;
			}
		}
	}
}
